import java.util.*;

public class BT {
	public enum STATUS { True, False, Progress };
	public enum LOOP{ WHILE, UNTIL, ONCE };
	
	public static abstract class Task{
		public long timestamp;
		public STATUS status;
		public int error_code = 0;
		public abstract STATUS step();
		public void reset(){}
		public void terminated(){}
		public String toString(){return toString("");}
		protected String toString(String tab){return tab+taskName();}
		protected String taskName(){ return this.getClass().getSimpleName(); }
	}
	
	public static class Seq extends Task{
		public String tname=null;
		ArrayList<Task> tasks = new ArrayList<Task>();
		
		public Seq(String tn){ this.tname = tn; }
		public Seq(){}
		
		public Seq add(Task t){ tasks.add(t); return this; }
		int current=0;
		public STATUS step(){
			if(current>=tasks.size()) return STATUS.True;
			Task task = tasks.get(current);
			task.timestamp = timestamp;
			STATUS res = task.step();
			this.error_code = task.error_code;
			if(res==STATUS.Progress){
				return STATUS.Progress;
			}else 
			if(res==STATUS.True){
				current++;
				if(current<tasks.size()) return STATUS.Progress;
				return STATUS.True;
			}else
			if(res==STATUS.False){
				current = tasks.size();
				return STATUS.False;
			}
			return STATUS.True;
		}
		public void terminated(){
			if(current>=tasks.size()) return;
			Task task = tasks.get(current);
			task.terminated();
		}
		public void reset() {
			current = 0;
			for(Task t: tasks) t.reset();
		}
		protected String toString(String tab){
			String res = tab+taskName()+"{";
			if(tasks.size()==0){
				res+="}";
				return res;
			}else{
				res+="\n";
			}
			for( Task t: tasks){
				res+=t.toString(tab+"   ");
				res+="\n";
			}
			res+=tab+"}";
			return res;
		}
		protected String taskName(){ if(tname==null) return "[->]"; return "[->]"+tname; }
	}
	public static class Sel extends Task{
		ArrayList<Task> tasks = new ArrayList<Task>();
		public String tname=null;
		
		public Sel(String tn){ this.tname = tn; }
		public Sel(){}
		
		public Sel add(Task t){ tasks.add(t); return this;}
		int current=0;
		public STATUS step(){
			if(current>=tasks.size()) return STATUS.False;
			Task task = tasks.get(current);
			task.timestamp = timestamp;
			STATUS res = task.step();
			this.error_code = task.error_code;
			if(res==STATUS.Progress){
				return STATUS.Progress;
			}else 
			if(res==STATUS.False){
				current++;
				if(current<tasks.size()) return STATUS.Progress;
				return STATUS.False;
			}else
			if(res==STATUS.True){
				current = tasks.size();
				return STATUS.True;
			}
			return STATUS.False;
		}
		public void terminated(){
			if(current>=tasks.size()) return;
			Task task = tasks.get(current);
			task.terminated();
		}
		public void reset() {
			current = 0;
			for(Task t: tasks) t.reset();
		}
		protected String toString(String tab){
			String res = tab+taskName()+"{";
			if(tasks.size()==0){
				res+="}";
				return res;
			}else{
				res+="\n";
			}
			for( Task t: tasks){
				res+=t.toString(tab+"   ");
				res+="\n";
			}
			res+=tab+"}";
			return res;
		}
		protected String taskName(){ if(tname==null) return "[??]"; return "[??]"+tname; }
	}
	public static class Swi extends Task{
		ArrayList<Task> tasks = new ArrayList<Task>();
		public String tname=null;
		
		public Swi(String tn){ this.tname = tn; }
		public Swi(){}
		
		public Swi add(Task t){ tasks.add(t); return this;}
		int current=0;
		public STATUS step(){
			if(current>=tasks.size()) return STATUS.False;
			Task task = tasks.get(current);
			task.timestamp = timestamp;
			STATUS res = task.step();
			this.error_code = task.error_code;
			if(res==STATUS.Progress){
				return STATUS.Progress;
			}else 
			if(res==STATUS.False){
				current=task.error_code;
				if(current<tasks.size()) return STATUS.Progress;
				return STATUS.False;
			}else
			if(res==STATUS.True){
				current = tasks.size();
				return STATUS.True;
			}
			return STATUS.False;
		}
		public void reset() {
			current = 0;
			for(Task t: tasks) t.reset();
		}
		public void terminated(){
			if(current>=tasks.size()) return;
			Task task = tasks.get(current);
			task.terminated();
		}
		protected String toString(String tab){
			String res = tab+taskName()+"{";
			if(tasks.size()==0){
				res+="}";
				return res;
			}else{
				res+="\n";
			}
			for( Task t: tasks){
				res+=t.toString(tab+"   ");
				res+="\n";
			}
			res+=tab+"}";
			return res;
		}
		protected String taskName(){ if(tname==null) return "[#?]"; return "[#?]"+tname; }
	}
	public static class Par extends Task{
		ArrayList<Task> tasks = new ArrayList<Task>();
		public String tname=null;
		
		public Par(String tn){ this.tname = tn; }
		public Par(){}
		
		public Par add(Task t){ tasks.add(t);  return this; }
		int current=0;
		public STATUS step(){
			if(tasks.size()<1) return STATUS.True;
			STATUS st = STATUS.Progress;
			ArrayList<Task> inprogress = new ArrayList<BT.Task>();
			for(Task task : tasks){
				task.timestamp = timestamp;
				STATUS s = task.step();
				if(s==STATUS.Progress){
					inprogress.add(task);
					continue;
				}
				if(st==STATUS.Progress){
					st = s;
					this.error_code = task.error_code;
				}
				//else if(st==STATUS.True && st==STATUS.False) st = s;
			}
			if(st!=STATUS.Progress){
				for(Task task: inprogress){
					task.terminated();
				}
			}
			return st;
		}
		public void reset() {
			for(Task t: tasks) t.reset();
		}
		public void terminated(){
			for(Task t: tasks) t.terminated();
		}
		protected String toString(String tab){
			String res = tab+taskName()+"{";
			if(tasks.size()==0){
				res+="}";
				return res;
			}else{
				res+="\n";
			}
			for( Task t: tasks){
				res+=t.toString(tab+"   ");
				res+="\n";
			}
			res+=tab+"}";
			return res;
		}
		protected String taskName(){ if(tname==null) return "[//]"; return "[//]"+tname; }
	}	
	public static abstract class Dec extends Task{
		Task task;
		public void set(Task t){ task = t; }
		public STATUS step(){
			if(task!=null) task.timestamp = timestamp;
			return decorator();
		}
		public void reset() {
			if(task!=null) task.reset();
		}
		public void terminated(){
			if(task!=null) task.terminated();
		}
		abstract protected STATUS decorator();
		protected String toString(String tab){
			String res = tab+"<"+taskName()+">";
			res+="\n";
			res+=task.toString(tab+"   ");
			//res+="\n";
			return res;
		}
		public static Task create(String name) {
			if(name.toLowerCase().equals("asis")) return new ASIS(null);
			if(name.toLowerCase().equals("!")) return new Not(null);
			if(name.toLowerCase().equals("not")) return new Not(null);
			if(name.toLowerCase().startsWith("f:")){return new Constant(null, false); }
			if(name.toLowerCase().equals("t")) return new Constant(null, true);
			if(name.toLowerCase().startsWith("l")) return new WhileTrue(null);
			if(name.toLowerCase().equals("!l!")) return new WhileFalse(null);
			if(name.toLowerCase().startsWith("whiletrue")) return new WhileTrue(null);
			if(name.toLowerCase().equals("whilefalse")) return new WhileFalse(null);
			if(name.toLowerCase().startsWith("whileok")) return new WhileTrue(null);
			if(name.toLowerCase().equals("untilok")) return new WhileFalse(null);
			return new ASIS(null);
		}
	}
	
	public static class ASIS extends Dec{
		public ASIS(Task t){ set(t); }
		protected STATUS decorator(){
			STATUS s = task.step();
			this.error_code = task.error_code;
			if(s==STATUS.Progress) return s;
			if(s==STATUS.True) return STATUS.True;
			return STATUS.False;
		}
	}

	public static class Not extends Dec{
		public Not(Task t){ set(t); }
		protected STATUS decorator(){
			STATUS s = task.step();
			this.error_code = task.error_code;
			if(s==STATUS.Progress) return s;
			if(s==STATUS.True) return STATUS.False;
			return STATUS.True;
		}
	}
	public static class Constant extends Dec{
		boolean val;
		int err;
		public Constant(Task t, boolean val){ this.val=val; this.err=0; set(t); }
		public Constant(Task t, int err){ this.err = err; this.val=false; set(t); }
		public Constant(Task t, boolean val, int err){ this.err = err; this.val=val; set(t); }
		protected STATUS decorator(){
			STATUS s = task.step();
			this.error_code = task.error_code;
			if(s==STATUS.Progress) return s;
			error_code = err;
			if(val) return STATUS.True;
			return STATUS.False;
		}
		protected String taskName() {
			return super.taskName()+":"+(val?"T":"F");
		}
	}
	public static class WhileTrue extends Dec{
		public WhileTrue(Task t){ set(t); }
		protected STATUS decorator(){
			STATUS s = task.step();
			this.error_code = task.error_code;
			if(s==STATUS.Progress) return s;
			if(s==STATUS.True){
				task.reset();
				return STATUS.Progress;
			}
			return STATUS.False;
		}
	}
	public static class WhileFalse extends Dec{
		public WhileFalse(Task t){ set(t); }
		protected STATUS decorator(){
			STATUS s = task.step();
			this.error_code = task.error_code;
			if(s==STATUS.Progress) return s;
			if(s==STATUS.False){
				task.reset();
				return STATUS.Progress;
			}
			return STATUS.True;
		}
	}
	
	public static abstract class Condition extends BT.Task{
		public static final boolean NOT = true;
		protected boolean is_not = false;
		protected LOOP loop = LOOP.ONCE;
		public Condition(LOOP loop, boolean not){
			is_not = not;
			this.loop = loop;
		}
		public Condition(LOOP loop){
			is_not = false;
			this.loop = loop;
		}
		public Condition(boolean not){
			is_not = not;
		}
		public Condition(){
			is_not = false;
		}
		
		abstract boolean check();
		
		public BT.STATUS step(){
			boolean check = check();
			if(is_not) check = !check;
			if(loop == LOOP.WHILE){
				if(check) return BT.STATUS.Progress;
				return BT.STATUS.True;
			}
			if(loop == LOOP.UNTIL){
				if(!check) return BT.STATUS.Progress;
				return BT.STATUS.True;
			}
			return check? BT.STATUS.True: BT.STATUS.False; 
		}
	}
	
	public static class Lookup extends Dec{
		LookupTable tbl;
		String taskname;
		public Lookup(LookupTable tbl, String taskname){ this.taskname = taskname; this.tbl = tbl; task = null; }
		protected STATUS decorator(){
			if(task==null && tbl.contains(taskname)){
				task = tbl.get(taskname);
				task.timestamp = timestamp;
				task.reset();
			}
			if(task==null){
				System.out.println("WARNING: "+toString()+": "+"task not found");
				error_code=1;
				return STATUS.False;
			}
			BT.STATUS st = task.step();
			this.error_code = task.error_code;
			return st;
		}
		protected String toString(String tab){
			if(tbl.contains(taskname)==false){
				return tab+"[@"+tbl.getClass().getSimpleName()+"]"+taskName()+" <-- WARNING: NOT FOUND";
			}
			String res = tab+"[@"+tbl.getClass().getSimpleName()+"]"+taskName();
			res += "\n" + tbl.get(taskname).toString(tab+"   ");
			return res;
		}
		protected String taskName(){ return taskname; }
	}
	
	static public class LookupTable {
		HashMap<String, Task> tbl = new HashMap<String, BT.Task>();
		ArrayList<LookupTable> parents = new ArrayList<BT.LookupTable>();
		public void addParent(LookupTable p){ parents.add(p); }
		public void removeParent(LookupTable p){ parents.remove(p); }
		public Task get(String taskname) {
			if(tbl.containsKey(taskname)) return tbl.get(taskname);
			for(LookupTable parent : parents){
				Task t = parent.get(taskname);
				if(t!=null) return t;
			}
			return null;
		}
		
		public void add(String tname, Task t){
			tbl.put(tname, t);
		}
		public void add(Task t){
			tbl.put(t.getClass().getSimpleName(), t);
		}
		public void remove(String tname){
			tbl.remove(tname);
		}
		public void remove(Task t){
			tbl.remove(t.getClass().getSimpleName());
		}
		public boolean contains(Task t){
			return contains(t.getClass().getSimpleName());
		}
		public boolean contains(String tname){
			boolean r = tbl.containsKey(tname);
			if(r) return true;
			for(LookupTable parent : parents){
				r = parent.contains(tname);
				if(r) return true;
			}
			return false;
		}
		public boolean containsLocal(Task t){
			return containsLocal(t.getClass().getSimpleName());
		}
		public boolean containsLocal(String tname){
			return tbl.containsKey(tname);
		}

	}
	
}
